﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Net;
using System.Net.Sockets;

namespace LiBLaB.Networking.Asynchronous
{
    public delegate void connectionAccepted_delegate(int client_index, bool status);
    public delegate void dataReceived_delegate_server(byte[] buffer, int client_index);
    public class Server
    {
        private List<Socket> clients = new List<Socket>();
        private int clients_counter = 0;
        connectionAccepted_delegate Accepted_delegate;
        public Server(int port, connectionAccepted_delegate connCallBack, dataReceived_delegate_server receiveCallBack)
        {
            Accepted_delegate = connCallBack;
            Socket server = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint iep = new IPEndPoint(IPAddress.Any, port);
            server.Bind(iep);
            server.Listen(10);
            server.BeginAccept(new AsyncCallback(AcceptConn), new object[3] { server, connCallBack, receiveCallBack });
        }

        void AcceptConn(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket oldserver = (Socket)state[0];
            connectionAccepted_delegate connCallBack = (connectionAccepted_delegate)state[1];
            dataReceived_delegate_server receiveCallBack = (dataReceived_delegate_server)state[2];
            clients.Add(oldserver.EndAccept(iar));
            connCallBack(clients_counter, true);
            Receive(receiveCallBack, clients_counter);
            oldserver.BeginAccept(new AsyncCallback(AcceptConn), state);
            clients_counter++;
        }

        public void Send(byte[] buffer, int client_index)
        {
            byte[] buffer_size = new byte[sizeof(int)];
            buffer_size = BitConverter.GetBytes(buffer.Length);
            clients[client_index].BeginSend(buffer_size, 0, buffer_size.Length, SocketFlags.None, new AsyncCallback(BeginSendCallBack), new object[2] { clients[client_index], buffer });
        }

        void BeginSendCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket client = (Socket)state[0];
            byte[] buffer = (byte[])state[1];
            int sent = client.EndSend(iar);
            client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallBack), state);
        }

        void SendCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket server = (Socket)state[0];
            int sent = server.EndSend(iar);
        }

        void Receive(dataReceived_delegate_server connCallBack, int client_index)
        {
            byte[] buffer = new byte[sizeof(int)];
            clients[client_index].BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(BeginReceiveCallBack), new object[4] { clients[client_index], buffer, connCallBack, client_index });
        }

        void BeginReceiveCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket client = (Socket)state[0];
            byte[] buffer = (byte[])state[1];
            dataReceived_delegate_server connCallBack = (dataReceived_delegate_server)state[2];
            int client_index = (int)state[3];

            try
            {
                int recv = client.EndReceive(iar);
                if (recv == 0)
                {
                    client.Close();
                    Accepted_delegate(client_index, false);
                }
                else
                {
                    int buffer_size = BitConverter.ToInt32(buffer, 0);
                    buffer = new byte[buffer_size];
                    client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), new object[4] { client, buffer, connCallBack, client_index });
                }
            }
            catch
            {
                Accepted_delegate(client_index, false);
            }
        }

        void ReceiveCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket server = (Socket)state[0];
            byte[] buffer = (byte[])state[1];
            dataReceived_delegate_server connCallBack = (dataReceived_delegate_server)state[2];
            int client_index = (int)state[3];
            int recv = server.EndReceive(iar);
            connCallBack(buffer, client_index);
            Receive(connCallBack, client_index);
        }
    }
}
